package net.nshc.kwkim.mycalc;

import java.util.*;

import net.nshc.kwkim.mycalc.activity.*;
import android.util.*;

public class PostFixExp {

	public Stack<Object> mStack;
	public static Stack<Object> rStack;

	public String infixToPostfix(String Value){

		String nToken;
		StringTokenizer parser;
		parser = new StringTokenizer(Value,"+-×÷pqsctflnxb,()~", true);
		Stack<Object> main = new Stack<Object>();
		Stack<Object> temp = new Stack<Object>();

		String operators = "+-×÷pqsctflnxb,(~";
		

		
		
		while(parser.hasMoreTokens()){
			nToken = parser.nextToken();
			if(MyCalcActivity.bLog)Log.d(LogDefine.LOG_CALCULATOIN, "nToken1 = "+nToken);
			
			for (int i = 0; i < temp.size(); i++) {
				if(MyCalcActivity.bLog)Log.d(LogDefine.LOG_CALCULATOIN, "in is isOperator temp["+i+"] = "+temp.get(i));
			}
			for (int i = 0; i < main.size(); i++) {
				if(MyCalcActivity.bLog)Log.d(LogDefine.LOG_CALCULATOIN, "in is isOperator main["+i+"] = "+main.get(i));
			}
			
			boolean isOperator = false;

			for(int i=0 ; i < operators.length(); i++){
				if(nToken.equals(operators.substring(i, i+1)))
					isOperator = true;
				if(MyCalcActivity.bLog)Log.d(LogDefine.LOG_CALCULATOIN, "nToken2 = "+nToken);
			}

			if(isOperator){
				
				if(MyCalcActivity.bLog)Log.d(LogDefine.LOG_CALCULATOIN, "nToken3 = "+nToken);
				
				while( (temp.size() > 0) && getPri(nToken,true) <= getPri(temp.peek(), false) ){
					if(MyCalcActivity.bLog)Log.d(LogDefine.LOG_CALCULATOIN, "temp_pop, main_push");
					main.push(temp.pop());
				}
				
				if(MyCalcActivity.bLog)Log.d(LogDefine.LOG_CALCULATOIN, "nToken4 = "+nToken);

				temp.push(nToken);
			}

			if(nToken.equals(")")){
				for (int i = 0; i < temp.size(); i++) {
					if(MyCalcActivity.bLog)Log.d(LogDefine.LOG_CALCULATOIN, "In to equals ')' temp["+i+"] = "+temp.get(i));
				}
				
				isOperator = true;
				boolean isEnd = false;
				while(temp.size() > 0 && isEnd == false){
					if(getPri(temp.peek(),false) == 0) isEnd = true;
					else{ 
						if(temp.peek().equals(",")){
							temp.pop();
						}else{
							main.push(temp.pop()); 
							if(MyCalcActivity.bLog)Log.d(LogDefine.LOG_CALCULATOIN, "temp_pop, main_push = "+main.peek());
						}
					}
				}
				if(MyCalcActivity.bLog)Log.d(LogDefine.LOG_CALCULATOIN, "temp_pop_temp_peek = "+temp.peek());
				temp.pop();
			}
			if(!isOperator)
				main.push(nToken);
		}
		while(temp.size() > 0){
			main.push(temp.pop());
		}

		String userView = "";

		for (int i = 0; i < main.size(); i++) {
			userView = userView + main.get(i);
		}

		Log.d(LogDefine.LOG_CALCULATOIN, "userView = "+userView);

		mStack = new Stack<Object>();
		mStack = main;

		return userView ;
	}

	private int getPri(Object ob , boolean cs) {  // 우선순위를 얻는 함수, cs는 icp or isp ..
		String operator = (String)ob;
		int pri = -1;
		if ( operator.equals(",") )
			pri = 11;
		if ( operator.equals("+") || operator.equals("-") )
			pri = 12;
		if ( operator.equals("×") || operator.equals("÷") )
			pri = 13;
		if ( operator.equals("q") || operator.equals("x") || operator.equals("~") || 
				operator.equals("s") || operator.equals("c") || operator.equals("t") || 
				operator.equals("f") || operator.equals("l") || operator.equals("n") )
			pri = 15;
		if ( operator.equals("b") || operator.equals("p") )
			pri = 14;
		if ( operator.equals(")") )
			pri = 19;
		if ( operator.equals("(") ) {
			if ( cs == true ) // icp를 묻는 경우
				pri =20;
			if ( cs == false ) // isp 를 묻는 경우
				pri = 0;
		}
		return pri;
	}

	public void debugStack(Stack<Object> stack){
		for (int i = 0; i < stack.size(); i++) {
			Log.d("DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD", "rStack["+i+"] = "+stack.get(i));
		}
		
		for (int i = 0; i < mStack.size(); i++) {
			Log.d("DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD", "mStack["+i+"] = "+mStack.get(i));
		}
	}

	public double result(String Value){ //단항연산자들은 순차적으로 그냥 넣어 버려도..괄호가 해결해주나 확인한 다음에 그냥 넣어버려.

		rStack = new Stack<Object>();

		for(int i = 0; i < mStack.size(); i++){
			Object t = mStack.get(i);
			double former = -1, latter = -1, temp;

			debugStack(rStack);

			if(t.equals("~")){

				if(rStack.isEmpty())
					return Double.NaN;

				double a = Double.parseDouble((String)rStack.pop());
				a = -1*a;
				rStack.push(a);

			}else if(t.equals("f")){

				if(rStack.isEmpty())
					return Double.NaN;

				double a = Double.parseDouble((String)rStack.pop());
				rStack.push(factorial((int)a));

			}else if(t.equals("q")){

				if(rStack.isEmpty())
					return Double.NaN;

				double a = Double.parseDouble((String)rStack.pop());
				rStack.push(Math.sqrt(a));

			}else if(t.equals("s")){

				if(rStack.isEmpty())
					return Double.NaN;

				double a = Double.parseDouble((String)rStack.pop());
				rStack.push(Math.sin(Math.toRadians(a)));

			}else if(t.equals("c")){

				if(rStack.isEmpty())
					return Double.NaN;

				double a = Double.parseDouble((String)rStack.pop());
				rStack.push(Math.cos(Math.toRadians(a)));

			}else if(t.equals("t")){

				if(rStack.isEmpty())
					return Double.NaN;

				double a = Double.parseDouble((String)rStack.pop());
				if(MyCalcActivity.bLog)Log.d(LogDefine.LOG_CALCULATOIN, "a = "+Math.toRadians(a));
				rStack.push(Math.tan(Math.toRadians(a)));

			}else if(t.equals("l")){

				if(rStack.isEmpty())
					return Double.NaN;

				double a = Double.parseDouble((String)rStack.pop());
				rStack.push(Math.log10(a));

			}else if(t.equals("n")){

				if(rStack.isEmpty())
					return Double.NaN;

				double a = Double.parseDouble((String)rStack.pop());
				rStack.push(Math.log(a));

			}else if(t.equals("x")){

				if(rStack.isEmpty())
					return Double.NaN;

				double a = Double.parseDouble((String)rStack.pop());
				rStack.push(Math.abs(a));

			}else if(t.equals("+") || t.equals("-") || t.equals("×") || t.equals("÷") || t.equals("p") || t.equals("b")){

				if(rStack.isEmpty())
					return Double.NaN;
				if(MyCalcActivity.bLog)Log.d(LogDefine.LOG_CALCULATOIN, "latter = "+rStack.peek());
				latter = Double.parseDouble((String)rStack.pop());

				if(rStack.isEmpty())
					return Double.NaN;
				if(MyCalcActivity.bLog)Log.d(LogDefine.LOG_CALCULATOIN, "former = "+rStack.peek());
				former = Double.parseDouble((String)rStack.pop());

				temp = -1;

				if( t.equals("+") ){
					temp = former+latter;
					if(MyCalcActivity.bLog)Log.d(LogDefine.LOG_CALCULATOIN, "plus = "+temp);
				}

				if( t.equals("-") ){
					temp = former-latter;
					if(MyCalcActivity.bLog)Log.d(LogDefine.LOG_CALCULATOIN, "minus = "+temp);
				}

				if( t.equals("×") ){
					temp = former*latter;
					if(MyCalcActivity.bLog)Log.d(LogDefine.LOG_CALCULATOIN, "multiply = "+temp);
				}

				if( t.equals("÷") ){
					temp = former/latter;
					if(MyCalcActivity.bLog)Log.d(LogDefine.LOG_CALCULATOIN, "divide = "+temp);
				}

				if( t.equals("p") ){
					temp = Math.pow(former,latter);
					if(MyCalcActivity.bLog)Log.d(LogDefine.LOG_CALCULATOIN, "power = "+temp);
				}

				if( t.equals("b") ){
					temp = combination((int)former,(int)latter);
					if(MyCalcActivity.bLog)Log.d(LogDefine.LOG_CALCULATOIN, "combination = "+temp);
				}

				rStack.push(temp);

			}else{

				rStack.push(t);

			}
		}

		if(rStack.isEmpty())
			return Double.NaN;

		return (Double)rStack.pop();
	}

	private Double combination(int m, int n)
	{
		return permutation(m, n)/factorial(n).intValue();
	}

	private Double permutation(int m, int n)
	{
		try{
			if(n == 1) return (double)(m);
			else return (double)(m * permutation(m - 1, n - 1));
		}catch (StackOverflowError e){
			return Double.NaN;
		}

	}

	private Double factorial(int n)
	{
		try{
			if(n == 1) return (double)1;
			else return (double)(n*factorial(n - 1));
		}catch (StackOverflowError e){
			return Double.MAX_VALUE-1;
		}

	}

}
